package util;

import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW_MATRIX;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_PROJECTION_MATRIX;
import static org.lwjgl.opengl.GL11.GL_VIEWPORT;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glGetFloat;
import static org.lwjgl.opengl.GL11.glGetInteger;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;
import static org.lwjgl.opengl.GL11.glPopMatrix;
import static org.lwjgl.opengl.GL11.glPushMatrix;
import static org.lwjgl.opengl.GL11.glRotatef;
import static org.lwjgl.opengl.GL11.glTranslated;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.util.glu.GLU.gluLookAt;
import static org.lwjgl.util.glu.GLU.gluPerspective;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;

import ui.UIConst;

public class Camera
{
	private IntBuffer _viewport;  
	private FloatBuffer _modelview;
	private FloatBuffer _projection;

	private float _aspectRatio = 0.0f;
	private float _FOV = UIConst.DEFAULT_FOV;
	
	public float _DEPTH = 175.0f;
	private float _xPos = UIConst.DEFAULT_POS_X;
	private float _yPos = UIConst.DEFAULT_POS_Y;
	private float _zPos = UIConst.DEFAULT_POS_Z;
	
	private float _xRot = UIConst.DEFAULT_ROT_X;
	private float _yRot = UIConst.DEFAULT_ROT_Y;
	private float _zRot = UIConst.DEFAULT_ROT_Z;
	
	public float[] getPosition()
	{
		float[] pos = new float[3];
		pos[0] = _xPos;
		pos[1] = _yPos;
		pos[2] = _zPos;
		return pos; 
	}
	
	public float[] getRotation()
	{
		float[] rot = new float[3];
		rot[0] = _xRot;
		rot[1] = _yRot;
		rot[2] = _zRot;
		return rot; 
	}
	
	public Camera()
	{
		_viewport = null;
		_modelview = null;
		_projection = null;
	}
	
	public void init()
	{
		glViewport(0, 0, UIConst.APP_WIDTH, UIConst.APP_HEIGHT);
		setProjectionMatrix(UIConst.APP_WIDTH, UIConst.APP_HEIGHT);
	}
	
	public void print()
	{
		System.out.println("Camera [ position=("+_xPos+" "+_yPos+" "+_zPos+") -- rotation("+_xRot+" "+_yRot+" "+_zRot+") ]");
	}
	
	protected void setProjectionMatrix(int width, int height)
	{
		_aspectRatio = (float)width/(float)height;

		glViewport(0, 0, width, height);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(_FOV, _aspectRatio, 0.01f, 10000.0f);
		glMatrixMode(GL_MODELVIEW);
	}
	
	public void set(float x, float y, float z)
	{
		_xPos = x;
		_yPos = y;
		_zPos = z;
	}
	
	public void translate(float x, float y, float z)
	{
		_xPos += x;
		_yPos += y;
		_zPos += z;
	}
	
	public void rotate(float x, float y, float z)
	{
		_xRot += x;
		_yRot += y;
		_zRot += z;
		
		if (_xRot > 360.0f) _xRot -= 360.0f;
		if (_yRot > 360.0f) _yRot -= 360.0f;
		if (_zRot > 360.0f) _zRot -= 360.0f;
	}
	
	public void reset()
	{
		 _xPos = UIConst.DEFAULT_POS_X;
		 _yPos = UIConst.DEFAULT_POS_Y;
		 _zPos = UIConst.DEFAULT_POS_Z;
		
		 _xRot = UIConst.DEFAULT_ROT_X;
		 _yRot = UIConst.DEFAULT_ROT_Y;
		 _zRot = UIConst.DEFAULT_ROT_Z;
	}

	public static void setOrthoON()
	{
		// prepare to render in 2D
		glDisable(GL_DEPTH_TEST);             // so 2D stuff stays on top of 3D scene
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();                            // preserve perspective view
		glLoadIdentity();                          // clear the perspective matrix
//		glOrtho(0, UIConst.APP_WIDTH, 0, UIConst.APP_HEIGHT,-1,1);  // turn on 2D
		glOrtho(0, UIConst.APP_WIDTH, UIConst.APP_HEIGHT,0,1,-1);  // turn on 2D
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();				// Preserve the Modelview Matrix
		glLoadIdentity();				// clear the Modelview Matrix
	}

	public static void setOrthoOFF()
	{
        // restore the original positions and views
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
        glEnable(GL_DEPTH_TEST);		// turn Depth Testing back on
	}
	
	public FloatBuffer getModelView() { return _modelview; }
	public FloatBuffer getProjection() { return _projection; }
	public IntBuffer getViewport() { return _viewport; }
	
	public void updateModelView()
	{
		if (_modelview == null) _modelview = BufferUtils.createFloatBuffer(16);
		glGetFloat(GL_MODELVIEW_MATRIX, _modelview);
	}
	
	public void updateProjection()
	{
		if (_projection == null) _projection = BufferUtils.createFloatBuffer(16);
		glGetFloat(GL_PROJECTION_MATRIX, _projection);
	}
	
	public void updateViewport()
	{
		if (_viewport == null) _viewport = BufferUtils.createIntBuffer(16);
		glGetInteger(GL_VIEWPORT, _viewport);
	}
	
	public void update()
	{
		glLoadIdentity();
		gluLookAt(0f, 0f, _DEPTH, 0f, 0f, 0f, 0f, 1f, 0f); // use x and y for camera source
		glTranslated(-_xPos, -_yPos, _zPos); //translate the screen
		glRotatef(_zRot, 0.0f, 0.0f, 1.0f);
		glRotatef(_yRot, 0.0f, 1.0f, 0.0f);
		glRotatef(_xRot, 1.0f, 0.0f, 0.0f);
		
		updateModelView();
		updateProjection();
		updateViewport();
	}
}